#ifndef GEDGEE_ONLYCPP_H
#define GEDGEE_ONLYCPP_H

#include "constant.h"

namespace gedgee {
    template <typename srcT, typename dstT>
    inline vector128 v128_cvt(vector128 vec) {
        return constant::v128_cvt<srcT, dstT>(vec);
    }

    template <typename T>
    inline vector128 v128_load(const T *p) {
        return constant::v128_load(p);
    }

    template <typename T>
    inline void v128_store(const T *p, vector128 vec) {
        constant::v128_store(p, vec);
    }

    template <typename T>
    inline vector128 v128_set(const T n) {
        return constant::v128_set(n);
    }

    template <typename T>
    inline vector128 v128_fill(const T n) {
        return constant::v128_fill(n);
    }

    template <typename T>
    inline T v128_get(vector128 vec) {
        return constant::v128_get<T>(vec);
    }

    template <typename T>
    inline vector128 v128_add(vector128 vec1, vector128 vec2) {
        return constant::v128_add<T>(vec1, vec2);
    }

    template <typename T>
    inline vector128 v128_sub(vector128 vec1, vector128 vec2) {
        return constant::v128_sub<T>(vec1, vec2);
    }

    template <typename T>
    inline vector128 v128_mul(vector128 vec1, vector128 vec2) {
        return constant::v128_mul<T>(vec1, vec2);
    }

    template <typename T>
    inline vector128 v128_div(vector128 vec1, vector128 vec2) {
        return constant::v128_div<T>(vec1, vec2);
    }

    template <typename T>
    inline vector128 v128_max(vector128 vec1, vector128 vec2) {
        return constant::v128_max<T>(vec1, vec2);
    }

    template <typename T>
    inline vector128 v128_min(vector128 vec1, vector128 vec2) {
        return constant::v128_min<T>(vec1, vec2);
    }

    template <typename T>
    inline vector128 v128_and(vector128 vec1, vector128 vec2) {
        return constant::v128_and<T>(vec1, vec2);
    }

    template <typename T>
    inline vector128 v128_or(vector128 vec1, vector128 vec2) {
        return constant::v128_or<T>(vec1, vec2);
    }

    template <typename T>
    inline vector128 v128_xor(vector128 vec1, vector128 vec2) {
        return constant::v128_xor<T>(vec1, vec2);
    }

    template <typename T>
    inline vector128 v128_andnot(vector128 vec1, vector128 vec2) {
        return constant::v128_andnot<T>(vec1, vec2);
    }

    template <typename T>
    inline vector128 v128_sll(vector128 vec, uint32_t count) {
        return constant::v128_sll<T>(vec, count);
    }

    template <typename T>
    inline vector128 v128_srl(vector128 vec, uint32_t count) {
        return constant::v128_srl<T>(vec, count);
    }

    template <typename T>
    inline vector128 v128_sra(vector128 vec, uint32_t count) {
        return constant::v128_sra<T>(vec, count);
    }

    template <typename T>
    inline vector128 v128_abs(vector128 vec) {
        if constexpr (_tool::is_u<T>()) {
            return vec;
        }

        if constexpr (_tool::is_i<T>()) {
            const vector128 mask = v128_sra<T>(vec, sizeof(T) * 8 - 1);
            const vector128 temp = v128_add<T>(vec, mask);
            return v128_xor<T>(temp, mask);
        }

        if constexpr (_tool::is_f32<T>()) {
            const vector128 output = v128_fill<uint32_t>(0x7FFFFFFF);
            return v128_and<uint32_t>(vec, output);
        }

        if constexpr (_tool::is_f64<T>()) {
            const vector128 output = v128_fill<uint64_t>(0x7FFFFFFFFFFFFFFF);
            return v128_and<uint64_t>(vec, output);
        }

        return constant::v128_abs<T>(vec);
    }

    template <typename T>
    inline vector128 v128_neg(vector128 vec) {
        if constexpr (_tool::is_u<T>()) {
                const vector128 zero = v128_fill<T>(0);
                return v128_sub<T>(zero, vec);
            }

            if constexpr (_tool::is_i<T>()) {
                constexpr int shift_bits = sizeof(T) * 8 - 1;
                const vector128 mask = v128_sra<T>(vec, shift_bits);
                const vector128 xor_result = v128_xor<T>(mask, vec);
                return v128_sub<T>(xor_result, mask);
            }

            if constexpr (_tool::is_f32<T>()) {
                const vector128 output = v128_fill<uint32_t>(0x7FFFFFFF);
                return v128_xor<uint32_t>(vec, output);
            }

            if constexpr (_tool::is_f64<T>()) {
                const vector128 output = v128_fill<uint64_t>(0x7FFFFFFFFFFFFFFF);
                return v128_xor<uint64_t>(vec, output);
            }

        return constant::v128_neg<T>(vec);
    }

    template <typename T>
    inline vector128 v128_sqrt(vector128 vec) {
        return constant::v128_sqrt<T>(vec);
    }

    template <typename T>
    inline vector128 v128_rsqrt(vector128 vec) {
        return constant::v128_rsqrt<T>(vec);
    }

    template <typename T>
    inline vector128 v128_rcp(vector128 vec) {
        return constant::v128_rcp<T>(vec);
    }

    template <typename T>
    inline vector128 v128_fma(vector128 vec, vector128 vmul, vector128 vadd) {
       return v128_add<T>(v128_mul<T>(vec, vmul), vadd);
    }

    template <typename T>
    inline vector128 v128_round(vector128 vec) {
        return constant::v128_round<T>(vec);
    }
}


#endif
